﻿using ElasticSearchTest.Model;
using Nest;
using PMS.Search.Domain.Common;
using PMS.Search.Domain.Entities;
using PMS.Search.Domain.QueryModel;
using PMS.Search.Elasticsearch;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ElasticSearchTest
{
    public class CourseEsService
    {
        private readonly EsClient _client;
        private readonly EsIndexConfig.Index _index = EsIndexConfig.Instance.CourseIndex;

        public CourseEsService(EsClient client)
        {
            _client = client;
        }
        public void Test()
        {
            var resp = CreateIndex();

            int type = 1;
            //var search = SearchAsync(new SearchCourseQueryModel("", 1, 10)).GetAwaiter().GetResult();

            var search2 = SearchAsync(new SearchCourseQueryModel("蝙蝠兔", 1, 100)).GetAwaiter().GetResult();

            var ss = search2.Data.Select(s => new
            {
                s.Title,
                s.OrgName
            }).ToList();
        }


        public async Task<PaginationModel<SearchCourse>> SearchAsync(SearchCourseQueryModel queryModel)
        {

            var keyword = queryModel.Keyword;
            var orderBy = queryModel.OrderBy;
            var type = queryModel.Type;
            var pageIndex = queryModel.PageIndex;
            var pageSize = queryModel.PageSize;

            int from = (pageIndex - 1) * pageSize;
            int size = pageSize;

            var search = new SearchDescriptor<SearchCourse>()
              .Index(_index.SearchIndex)
              .From(from)
              .Size(size)
              .Sort(SearchPUV.GetSortDesc<SearchCourse>(orderBy, "modifyDateTime"))
              ;

            var shouldQuerys = new List<Func<QueryContainerDescriptor<SearchCourse>, QueryContainer>>
            {
                q => q.Term(t => t.Field("title").Value(keyword).Boost(10))
            };

            var mustQuerys = new List<Func<QueryContainerDescriptor<SearchCourse>, QueryContainer>>
            {
                m => m.Term(t => t.Field(f => f.IsDeleted).Value(false)),
                //m => m.Term(t => t.Field(f => f.IsValid).Value(1)),
                //m => m.Term(t => t.Field(f => f.Status).Value(1)),
            };

            if (!queryModel.SearchTitleOnly)
            {
                if (queryModel.QueryEsKeywords.Any())
                {
                    var innerShoulds = NestHelper.KeywordsToNest<SearchCourse>(
                        queryModel.QueryEsKeywords,
                        queryModel.IsMatchAllWord,
                        Infer.Fields("title.pinyin", "title.keyword", "orgName.pinyin", "orgName.keyword"),
                        Infer.Fields("title.cntext", "orgName.cntext"));
                    mustQuerys.Add(q => q.Bool(b => b.Should(innerShoulds)));

                    shouldQuerys.Add(q => q.Term(t => t.Field("orgName").Value(keyword).Boost(10)));
                }
            }
            else
            {
                if (queryModel.QueryEsKeywords.Any())
                {
                    var innerShoulds = NestHelper.KeywordsToNest<SearchCourse>(
                        queryModel.QueryEsKeywords,
                        queryModel.IsMatchAllWord,
                        Infer.Fields("title.pinyin", "title.keyword"),
                        Infer.Fields("title.cntext"));
                    mustQuerys.Add(q => q.Bool(b => b.Should(innerShoulds)));
                }
            }


            if (type != null)
            {
                mustQuerys.Add(m => m.Term(t => t.Field(f => f.Type).Value(type)));
            }


            search.Query(q =>
                q.Bool(b =>
                    b.Must(mustQuerys).Should(shouldQuerys)
                ));

            var result = await _client.GetClient().SearchAsync<SearchCourse>(search);
            var data = result.Hits.Select(s => s.Source).ToList();
            return PaginationModel.Build(data, result.Total);
        }

        /// <summary>
        /// 课程
        /// </summary>
        public CreateIndexResponse CreateIndex()
        {
            var resp = _client.GetClient().Indices.Delete(_index.Name);
            var createIndexResponse = _client.GetClient().Indices
                .Create(_index.Name, c => c
                .Aliases(ad => ad.Alias(_index.Alias))
                .Settings(s => s
                    .Setting("max_ngram_diff", 20)
                    .Setting("max_result_window", 500000)
                    .Analysis(AnalyzeBuilder.GetAnalysis())
                )
                .Map<SearchCourse>(mm => mm
                .Properties(p => p
                    .Keyword(t => t.Name(n => n.Id))
                    .Number(t => t.Name(n => n.No))
                    .Text(t => t.Name(n => n.Title)
                         .Fields(f => f
                                .Text(ft
                                    => ft.Name("cntext")
                                    .Analyzer(AnalyzeBuilder.IkNgramAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkSearchAnalyzer))
                                .Text(ft
                                    => ft.Name("pinyin")
                                    .Analyzer(AnalyzeBuilder.IkPinyinAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkPinyinSearchAnalyzer))
                                .Text(ft
                                    => ft.Name("keyword")
                                    .Analyzer(AnalyzeBuilder.KeywordPinyinNgramAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkSearchAnalyzer))
                        )
                    )
                    .Text(t => t.Name(n => n.Subtitle)
                         .Fields(f => f
                                .Text(ft
                                    => ft.Name("cntext")
                                    .Analyzer(AnalyzeBuilder.IkNgramAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkSearchAnalyzer))
                                .Text(ft
                                    => ft.Name("pinyin")
                                    .Analyzer(AnalyzeBuilder.IkPinyinAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkPinyinSearchAnalyzer))
                        )
                    )
                    .Number(t => t.Name(n => n.Status))
                    .Number(t => t.Name(n => n.Price))
                    .Keyword(t => t.Name(n => n.Mode).Fields(ff => ff.Number(fk => fk.Name("text"))))
                    .Number(t => t.Name(n => n.Subject))
                    .Number(t => t.Name(n => n.MinAge))
                    .Number(t => t.Name(n => n.MaxAge))
                    .Keyword(t => t.Name(n => n.OrgId))
                    .Number(t => t.Name(n => n.OrgNo))
                    .Text(t => t.Name(n => n.OrgName)
                         .Fields(f => f
                                .Text(ft
                                    => ft.Name("cntext")
                                    .Analyzer(AnalyzeBuilder.IkNgramAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkSearchAnalyzer))
                                .Text(ft
                                    => ft.Name("pinyin")
                                    .Analyzer(AnalyzeBuilder.IkPinyinAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkPinyinSearchAnalyzer))
                                .Text(ft
                                    => ft.Name("keyword")
                                    .Analyzer(AnalyzeBuilder.KeywordPinyinNgramAnalyzer)
                                    .SearchAnalyzer(AnalyzeBuilder.IkSearchAnalyzer))
                        )
                    )
                    .Number(t => t.Name(n => n.Authentication))
                    .Text(t => t.Name(n => n.Desc))
                    .Text(t => t.Name(n => n.SubDesc))
                    .Number(t => t.Name(n => n.OrgStatus))
                    .Date(t => t.Name(n => n.ModifyDateTime))
                    .Number(t => t.Name(n => n.IsValid))
                    .Boolean(t => t.Name(n => n.IsDeleted))
                    .Number(t => t.Name(n => n.OrgIsValid))
                    .Number(t => t.Name(n => n.Type))
                    .Number(t => t.Name(n => n.Pv))
                    .Number(t => t.Name(n => n.Uv))
                    ))
            );
            return createIndexResponse;
        }
    }
}
